home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1 / Nebula One.iso / Utilities / Converters / Convert_MacPaint / Source / macpaintConverter.m < prev    next >
Text File  |  1995-06-12  |  13KB  |  367 lines

  1. /***********************************************************************\
  2. Converter class for Convert MacPaint which converts MacPaint files to PostScript files.
  3. Copyright (C) 1993 David John Burrowes
  4.  
  5. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version.
  6.  
  7. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
  8.  
  9. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  10.  
  11. The author, David John Burrowes, can be reached at:
  12.     davidjohn@kira.net.netcom.com
  13.     David John Burrowes
  14.     1926 Ivy #10
  15.     San Mateo, CA 94403-1367
  16. \***********************************************************************/
  17.  
  18.  
  19. #import "macpaintConverter.h"
  20. #import "File.h"
  21. #import "PSFile.h"
  22. #import <strings.h>
  23. #import <stdio.h>
  24. #import <stdlib.h>        // for malloc and free
  25. #import <sys/param.h>    // for maxpathlen
  26. #include <time.h>
  27.  
  28. @implementation macpaintConverter
  29.  
  30. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  31. //    Method:    UnpackFrom:To:
  32. //    Parameters:
  33. //        A buffer to unpack data from
  34. //        A buffer to put data into
  35. //    Returns:
  36. //        self
  37. //    Stores:    n/a
  38. //    Description:
  39. //        This takes a buffer in memory, and assumes it contains a macpaint image
  40. //        in packbits form.  This then takes that packed data, and unpacks it into
  41. //        the To: buffer.  Note that it inverts all the image data as it goes, because
  42. //        the mac uses 1 for white, and PS 1 for black (or the reverse or something).
  43. //    Bugs:
  44. //        none, of course.  Actually, there's room for tons.  The most obvious thing is that
  45. //        this does nothing to assure the 'goodness' of the data being passed to it.
  46. //        it just blindly decompresses until it has unpacked 51840 bytes (the size of
  47. //        an unpacked mac paint image)
  48. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  49. - UnpackFrom: (ByteString) sourceData To: (ByteString) destData
  50. {
  51.     Byte flag;
  52.     Byte dataByte;
  53.     Integer index;                        // counter
  54.     Integer sourceIndex = 0, destIndex = 0;    // counters
  55.     Integer BytesDone = 0;    // Number of bytes put into unpacked buffer
  56.     Integer    nextPeak = 0;
  57.  
  58.     while (BytesDone < 51840)
  59.     {
  60.         //
  61.         //    Let's not call our manager EVERY byte!.
  62.         //
  63.         if (BytesDone > nextPeak)
  64.         {
  65.             [myManager   SetPercentageDone:  (BytesDone / 518.4) * Weighting];
  66.             nextPeak += 5184;
  67.         }
  68.  
  69.         //
  70.         //    Read in a byte, which tells us about the following data...
  71.         //    if < 128, we will copy bytes literally, if > 128, will copy the next
  72.         //    byte a numnber of times.
  73.         //
  74.         flag = sourceData[sourceIndex];
  75.         sourceIndex++;
  76.         if (flag < 128)
  77.         {
  78.             //
  79.             // copy one more than the number of Bytes literally.
  80.             //
  81.             flag++;
  82.             for (index = 0; index < flag; index++)
  83.             {
  84.                 // invert the data
  85.                 destData[destIndex] =  ~sourceData[sourceIndex];
  86.                 sourceIndex++;
  87.                 destIndex++;
  88.             }
  89.             BytesDone += flag;
  90.         }
  91.         else
  92.         {
  93.             //
  94.             //    copy next Byte 257-flag times
  95.             //
  96.             dataByte = ~sourceData[sourceIndex];
  97.             sourceIndex++;
  98.             for (index = 0; index < 257 -flag; index ++)
  99.             {
  100.                 destData[destIndex] = dataByte;
  101.                 destIndex++;
  102.             }
  103.             BytesDone += 257-flag;
  104.         }
  105.     }
  106.     return self;
  107. }
  108.  
  109.  
  110. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  111. // 
  112. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  113. -(int) PackDataFrom: (ByteString) source To: (ByteString) dest
  114. {
  115.     Boolean    runs = NO;
  116.     Integer    numTraversedBytes;
  117.     Integer    startByte = 0;
  118.     Integer    lineLength = 72;
  119.     Integer    index = 0;
  120.     Integer    destIndex = 0;
  121.     Integer    ctr;
  122.     Integer    accumulated;
  123.     Integer    runCount;
  124.     
  125.  
  126.     accumulated = 0;
  127.     do
  128.     {
  129.         startByte = index;
  130.         /* first Byte is always assumed to be OK.  Safe assumption.  Either
  131.             we are starting (Byte 0 should always be good), or we just dumped
  132.             some data, at which index points to the first Byte that did not match
  133.             that pattern, and so is good to use for starting with the next */
  134.         index++;
  135.         accumulated++;
  136.         // Try to accumulate a run
  137.         while ((source[index-1] == source[index]) &&
  138.                 ((index-startByte) < 128) &&
  139.                 (accumulated < lineLength))
  140.         {
  141.             index++;
  142.             accumulated++;
  143.         }
  144.         if ((index-startByte) > 2)
  145.             runs = YES;
  146.         else
  147.         {
  148.             runCount = 1;
  149.             // not enough to justify a run, so carry on. as a litteral sequence of Bytes
  150.             while ((source[index-1] != source[index]) &&
  151.                     ((index-startByte) < 128) &&
  152.                     (accumulated < lineLength))
  153.             {
  154.                 index++;
  155.                 accumulated++;
  156.             }
  157. //
  158. //    @@ bug: in the above, we should be checking to allow for accumulating
  159. //    @@ runs with length 2, since they are too short to be treated as runs.
  160. //    @@ the present code works, but just isn't ideal.
  161. //
  162.             if (accumulated < lineLength) // We ran into a match, not the end
  163.             {
  164.                 index--;    // Thus, back up over 1 of the 2 Bytes.
  165.                 accumulated--;
  166.             }
  167.             runs = NO;
  168.         }
  169.         numTraversedBytes = index-startByte;
  170.         if (runs == YES) 
  171.         {
  172.             dest[destIndex] = 257-numTraversedBytes;
  173.             destIndex++;
  174.             dest[destIndex] = source[startByte];
  175.             destIndex++;
  176.         }
  177.         else
  178.         {
  179.             dest[destIndex] = numTraversedBytes - 1;
  180.             destIndex++;
  181.             for (ctr = startByte; ctr < startByte+numTraversedBytes; ctr++)
  182.             {
  183.                 dest[destIndex] = source[ctr];
  184.                 destIndex++;
  185.             }
  186.         }
  187.     }
  188.     while (accumulated < lineLength);
  189.     return destIndex;    /* return a number that is the num of bytes in the destination */
  190. }
  191.  
  192.  
  193.  
  194. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  195. //    Routine:    SetPacking:AndPath:
  196. //    Parameters:
  197. //        a flag indicating if we should pack data that we write out
  198. //        a string indicating where various needed files are to be found
  199. //    Returns: 
  200. //        self
  201. //    Description:
  202. //        This sets up two instance variables, using values we have been provided:
  203. //        - whether output data should be packed or not (yes or no)
  204. //        - Where to find the ps files this object needs to do it's conversion.  This should
  205. //            be a legal path name, or null (indicating the current working directory).
  206. //    Bugs:
  207. //        this does NOT check for the validity of the path.
  208. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  209. - SetPacking: (Boolean) shouldPack AndPath: (CString) thepath
  210. {
  211.     if (pathToIncludes != NullCString)
  212.         FreeCString(pathToIncludes);
  213.  
  214.     pathToIncludes= NewCString(strlen(thepath));
  215.     strcpy(pathToIncludes, thepath);
  216.     
  217.     packing = shouldPack;
  218.     return self;
  219. }
  220.  
  221.  
  222. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  223. //    Method:        free
  224. //    Parameters:    none
  225. //    Returns:     self
  226. //    Description:
  227. //        This frees the current object, which entails merely disposing of the string
  228. //        in the instance variables
  229. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  230. - free
  231. {
  232.     FreeCString(pathToIncludes);
  233.     return [super free];
  234. }
  235.  
  236.  
  237. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  238. //    Method:        convert:To:
  239. //    Parameters:    a source object  and a destination file object (a PSFile object)
  240. //    Returns:        self
  241. //    Stores:        n/a
  242. //    Description:
  243. //    This method takes the contents of the source file (a MacPaint file, we assume)
  244. //    and changes the contents into an eps file, which is written to the destination file.
  245. //    This relies upon the parameters passed to the init method to determine whether to
  246. //    pack the data
  247. //    This depends on a set of files to provide the PS code for the resulting eps file.
  248. //    Note: When it comes to reporting the percent done, we ignore all the preliminary
  249. //    file reading and writing, and only report on the precentages through the unpacking
  250. //    and packing.  The Weight value is used to aid in reporting accurate results.  When
  251. //    we are not re-packing the data, we set weight to 1.  Otherwise, we set it to 1.
  252. //    One might also ask: Why bother to unpack data we're about to re-pack anyway.
  253. //    the answer is that we need to invert it anyway.  True, we could invert it wihtout
  254. //    going through the effort of unpacking it, but hey, it's easier this way... =)   =(
  255. //    Bugs:
  256. //        Inadequate error responses are provided!
  257. //        Some others I don't know of surely exist.  This object 'grew' too much.  not enough
  258. //        planning
  259. //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  260. - Convert: sourceFile To: destFile
  261. {
  262.     PositiveInteger    newPackedSize, imagePackedSize;
  263.     Integer        thetime;
  264.     Instance        headerInfo, prologueCode;
  265.     CString        comment    = NewCString(270);
  266.     CString        tempPath = NewCString(MAXPATHLEN);
  267.     ByteString    unpackedImage, packedImage, newPackedImage;
  268.     Integer        scanlines;
  269.     Integer        packedLoc;
  270.     
  271.     //
  272.     //    Locate 'include' files of PS code we need
  273.     //    open them, and if all goes well, proceed.
  274.     //
  275.     sprintf(tempPath, "%s/%s", pathToIncludes, "PSstuff/EPS_header");
  276.     headerInfo = [[PSFile alloc] initAndUse: tempPath];
  277.     if (packing == YES)
  278.     {
  279.         sprintf(tempPath, "%s/%s", pathToIncludes, "PSstuff/PackedCode");
  280.         prologueCode = [[PSFile alloc] initAndUse: tempPath];
  281.     }
  282.     else
  283.     {
  284.         sprintf(tempPath, "%s/%s", pathToIncludes, "PSstuff/UnpackedCode");
  285.         prologueCode = [[PSFile alloc] initAndUse: tempPath];
  286.     }
  287.     [headerInfo OpenFor: FILE_READ];
  288.     [prologueCode OpenFor: FILE_READ];
  289.     
  290.     if (([headerInfo  GetErrorCode] != 0) || ([prologueCode  GetErrorCode] != 0))
  291.         [self StoreErrorCode: ERR_BADINCLUDES
  292.             AndText: "Could not load a necessary included PS file"];
  293.     else
  294.     {
  295.         //
  296.         //    Write the header and prolog of the destinatin file.
  297.         //
  298.         [destFile AppendFrom: headerInfo];
  299.         thetime = time(NULL);
  300.         sprintf(comment, "CreationDate: (%s", (char*) ctime(&thetime));         comment[strlen(comment)-1] = EndOfCString; // ctime puts a \n on which we don't wan
  301.         strcat(comment, ")");
  302.         [destFile WriteDSCComment: comment];
  303.         sprintf(comment, "Title: (%s)", [sourceFile GetFilename]);
  304.         [destFile WriteDSCComment: comment];
  305.         [destFile WriteDSCComment: "EndComments"];
  306.         [destFile WritePSLine: "\nsave\n"];
  307.         [destFile WriteDSCComment: "BeginProlog"];
  308.         //
  309.         [destFile AppendFrom: prologueCode];
  310.         [destFile WriteDSCComment: "EndProlog"];
  311.         [destFile WritePSLine: "\nshowMPimage"];
  312.         //
  313.         //    Write the hex ddata of the image to the output.
  314.         //
  315.         imagePackedSize = [sourceFile FileSize] - 512;
  316.         packedImage = NewByteString(imagePackedSize);
  317.         unpackedImage = NewByteString(52000); // allocate a listtle extra (over 51840)
  318.         newPackedImage = NewByteString(52000); // allocate as much as we'll need
  319.         [sourceFile MoveTo:  512];
  320.         [sourceFile Read: imagePackedSize BytesInto: packedImage];
  321.         if (packing == YES)
  322.             Weighting = 0.5;
  323.         else
  324.             Weighting = 1;
  325.         Weighting = Weighting * .75;
  326.         [self UnpackFrom: packedImage To: unpackedImage];
  327.         if (packing == YES)
  328.         {
  329.             packedLoc    = 0;
  330.             for (scanlines = 0;  scanlines < 720; scanlines++)
  331.             {
  332.                 //
  333.                 //    Let's not call our manager EVERY scanline.
  334.                 //
  335.                 if ((scanlines %  72) == 0)
  336.                     [myManager   SetPercentageDone: 40+ (((scanlines / 7.2)*.6))];
  337.                 newPackedSize = [self PackDataFrom: &(unpackedImage[scanlines*72])
  338.                          To: newPackedImage];
  339.                 [destFile Write: newPackedSize BytesOfHexDataFrom: newPackedImage];
  340.             }
  341.             // Add 80> to mark the end of file for an ASCIIHexDecode & Runlength filter 
  342.             [destFile WritePSLine: "80>"];            }
  343.         else
  344.         {
  345.             [myManager   SetPercentageDone:  75];
  346.             for (scanlines = 0;  scanlines < 720; scanlines++) // total 51840 bytes
  347.                 [destFile Write: 72 BytesOfHexDataFrom: &(unpackedImage[scanlines*72])];
  348.             [destFile WritePSLine: ">"];    // Add > to mark EOF for ASCIIHexDecode filter 
  349.         }
  350.         //
  351.         //    Finish off the ps code.
  352.         //    93.01.31    djb    Moved EOF after the restore.  Somehow I had it showpage, eof, restore
  353.         [destFile WritePSLine: "showpage"];
  354.         [destFile WritePSLine: "restore"];
  355.         [destFile WriteDSCComment: "EOF"];
  356.         [self StoreErrorCode: ERR_OK AndText: "Simplisitically assumed all went well"];
  357.         FreeByteString(unpackedImage);
  358.         FreeByteString(newPackedImage);
  359.         FreeByteString(packedImage);
  360.     }
  361.     [prologueCode Close];
  362.     [headerInfo Close];
  363.     return self;
  364. }
  365.  
  366. @end
  367.